import React, { lazy, Suspense, useCallback, useEffect, useMemo, useReducer, useState } from 'react'
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'
import logReducer, { initLogState, LoginContext } from 'reducers/log'
import { MODE, playListLocalStorage } from 'helpers/play'
import playMusicReducer, { ACTIONS, initCurMusic, PlayMusicContext, AudioCtx } from 'reducers/playMusic'
import useAudio from 'hooks/useAudio'
import Layout from 'components/Layout'
import ROUTES from 'constants/routes'

const Discovery = lazy(() => import('./Discovery'))
const Videos = lazy(() => import('./Videos'))
const SongListDetail = lazy(() => import('./SonglistDetail'))
const Search = lazy(() => import('./Search'))

const App = () => {
  const [logState, logDispatch] = useReducer(logReducer, initLogState)
  const [playState, playDispatch] = useReducer(playMusicReducer, initCurMusic)
  const [playList, setPlayList] = useState([])
  const { musicUrl, musicId, playMode } = playState

  useEffect(() => {
    setPlayList(() => playListLocalStorage.getItem())
  }, [])
  const [audio, audioState, audioControls, audioRef] = useAudio({
    src: musicUrl,
    autoPlay: true,
    onError: () => {
      if (playMode === MODE.SINGLE_CYCLE) {
        return
      }
      playNextMusic()
    },
    onEnded: () => playNextMusic(),
  })
  const { current } = audioRef
  const audioInfo = useMemo(() => {
    return {
      audio,
      state: audioState,
      controls: audioControls,
      ref: current,
    }
  }, [musicId, current?.currentTime])

  const playNextMusic = useCallback(() => {
    const len = playList.length
    switch (playMode) {
      case MODE.PLAY_IN_ORDER:
        if (len) {
          const idx = playList.findIndex(({ id }) => id === musicId)
          const nextIdx = idx > -1 ? (idx + 1) % len : 0
          playMusic(nextIdx)
        }
        return
      case MODE.SHUFFLE_PLAYBACK:
        if (len) {
          const random = Math.floor(Math.random() * len)
          playMusic(random)
        }
        return
      case MODE.SINGLE_CYCLE:
        audioControls.play()
        return
      default:
        return
    }
  }, [playList])
  const playMusic = useCallback(
    (i: number) => {
      const item: { id?: string } = playList[i]
      playDispatch({
        type: ACTIONS.PLAY,
        payload: {
          musicId: item.id,
          music: item,
        },
      })
    },
    [playList],
  )
  return (
    <BrowserRouter>
      <LoginContext.Provider value={[logState, logDispatch]}>
        <PlayMusicContext.Provider value={[playState, playDispatch]}>
          <AudioCtx.Provider value={audioInfo}>
            <Layout>
              {audio}
              <Suspense fallback={null}>
                <Switch>
                  <Route path={ROUTES.DISCOVERY} component={Discovery} />
                  <Route path={ROUTES.SEARCH} component={Search} />
                  <Route path={ROUTES.VIDEOS} component={Videos} />
                  <Route path={ROUTES.SONG_LIST_DETAIL} component={SongListDetail} />
                  <Redirect from={ROUTES.ROOT} to={ROUTES.DISCOVERY} />
                </Switch>
              </Suspense>
            </Layout>
          </AudioCtx.Provider>
        </PlayMusicContext.Provider>
      </LoginContext.Provider>
    </BrowserRouter>
  )
}
export default App
